import type { AxiosRequestConfig, AxiosResponse } from "axios";
import Axios, { type AxiosInstance } from "axios";
import { ElLoading, ElMessage, ElMessageBox } from "element-plus";

import envConfig from '@/settingConfig/envConfig';

const { VITE_APP_BASE_API } = envConfig

let lapse = false;
/**
 * ElLoading 配置
 */
const loadingOptions = {
    text: "加载中,请稍等...",
};

/**
 * ElLoading 实例
 */
let loadingInstance: any = null;

/**
 * 请求默认配置
 */
const defaultOptions = {
    carryUserToken: true, // 携带用户token
    cancelDuplicateRequest: true, // 是否开启取消重复请求
    formatResponseData: true, // 是否格式化返回具体的数据，而非返回原生response
    showLoading: false, // 是否开启loading层效果
    showErrorMessage: true, // 是否开启错误信息展示
    removeEmptyParameter: false, // 移除为空字符串的参数字段 例： {type: ''},
};

/**
 * 请求参数类型
 */
interface ReqOptions {
    method?: string; // 请求方法
    url: string; // 请求路径
    data?: object; // 请求参数
    carryUserToken?: boolean; // 携带用户token
    cancelDuplicateRequest?: boolean; // 是否开启取消重复请求
    formatResponseData?: boolean; // 是否格式化返回具体的数据，而非返回原生response
    showLoading?: boolean; // 是否开启loading层效果
    showErrorMessage?: boolean; // 是否开启错误信息展示
    removeEmptyStringParameters?: boolean; // 移除为空字符串的参数字段 例： {type: ''},
}

/**
 * 后端返回结构
 */
interface MyResponse<T = any> {
    code: number;
    data: T;
    isShowMsg?: boolean;
    msg?: string;
}

/**
 * 请求中队列
 */
const requestQueue = new Map();

// 后续的请求队列
let requestList = <any>[];

/**
 * 请求拦截函数
 * @param config Axios配置对象
 * @returns config
 */
const requestInterceptionFunction = (config: any) => {
    return config;
};

/**
 * 响应拦截函数
 * @param response Axios响应对象
 * @returns config
 */
const responseInterceptionFunction = async (
    response: AxiosResponse<MyResponse>,
    axios: any
) => {
    const { showErrorMessage, formatResponseData, showLoading } =
        response.config as AxiosResponse & ReqOptions;
    // removePending(response.config)
    showLoading && closeLoading();

    const { code, message: msg } = response.data;

    // 如果在刷新token时仍然是过期token则跳转登陆页面
    if (code == 40104 && lapse == true) {
        cancelAllRequests();
        ElMessageBox.confirm("会话已失效请重新登录", "提示", {
            confirmButtonText: "确定",
            showCancelButton: false,
            showClose: false,
            closeOnPressEscape: false,
            closeOnClickModal: false,
            type: "warning",
        }).then(() => {
            lapse = false;
        });
    }
    switch (+code) {
        case 200:
            // 展示成功消息
            // isShowMsg && ElMessage({ message: msg, type: 'success' })
            return formatResponseData ? response.data?.data : response.data;
        default:
            // 展示错误消息
            showErrorMessage &&
                ElMessage({ message: msg ?? "响应出错，请稍后重试", type: "error" });
            return Promise.reject(response);
    }
};

/**
 * 处理异常
 * @param {*} error
 */
function httpErrorStatusHandle(error: any) {
    // 处理被取消的请求
    if (Axios.isCancel(error))
        return console.error("请求的重复请求:" + error.message);
    let message = "";
    if (error && error.response) {
        switch (error.response.status) {
            case 302:
                message = "接口重定向了！";
                break;
            case 400:
                message = "参数不正确！";
                break;
            case 401:
                message = "您未登录，或者登录已经超时，请先登录！";
                break;
            case 403:
                message = "您没有权限操作！";
                break;
            case 404:
                message = `请求地址出错: ${error.response.config.url}`;
                break; // 在正确域名下
            case 408:
                message = "请求超时！";
                break;
            case 409:
                message = "系统已存在相同数据！";
                break;
            case 500:
                message = "服务器内部错误！";
                break;
            case 501:
                message = "服务未实现！";
                break;
            case 502:
                message = "网关错误！";
                break;
            case 503:
                message = "服务不可用！";
                break;
            case 504:
                message = "服务暂时无法访问，请稍后再试！";
                break;
            case 505:
                message = "HTTP版本不受支持！";
                break;
            default:
                message = "异常问题，请联系管理员！";
                break;
        }
    }
    error.message.includes("timeout") && (message = "网络请求超时！");
    error.message.includes("Network") &&
        (message = window.navigator.onLine ? "服务端异常！" : "您断网了！");
    ElMessage({
        type: "error",
        message,
    });
}

/**
 * 移除空字符串参数
 * @param data 源对象
 * @returns 移除后的对象浅拷贝
 */
const handleRemoveEmptyParameter = (data: object | undefined) => {
    if (!data) {
        return;
    }
    const shallowCopy = Object.assign(data);
    Object.keys(shallowCopy).forEach((key) => {
        shallowCopy[key] === "" && (shallowCopy[key] = null);
    });
    return shallowCopy;
};



/**
 * 取消所有进行中请求
 */
function cancelAllRequests() {
    const reqQueue = requestQueue.entries();
    for (const [key, cancelToken] of reqQueue) {
        cancelToken(key);
    }
    requestQueue.clear();
}

/**
 * 删除重复的请求
 */
const removePending = (config: AxiosRequestConfig) => {
    const pendingKey = getPendingKey(config);
    if (requestQueue.has(pendingKey)) {
        const cancelToken = requestQueue.get(pendingKey);
        cancelToken(pendingKey);
        requestQueue.delete(pendingKey);
    }
};

/**
 * 开启弹窗
 */
const openLoading = () => {
    loadingInstance = ElLoading.service(loadingOptions);
};

/**
 * 关闭弹窗
 */
const closeLoading = () => {
    loadingInstance && loadingInstance.close();
};

/**
 * 生成每个请求的唯一key
 * @param config
 * @returns pendingKey
 */
const getPendingKey = (config: AxiosRequestConfig) => {
    let { data } = config;
    const { url, method, params } = config;
    typeof data === "string" && (data = JSON.parse(data)); // response里面返回的config.data是个字符串对象
    return [url, method, JSON.stringify(params), JSON.stringify(data)].join("&");
};

/**
 * 应用Axios请求、响应拦截器
 * @param {*} axios 实例
 * @param {*} config 配置
 */
const useAxiosInterceptors = (axios: AxiosInstance, config: ReqOptions) => {
    // 请求、响应拦截器
    axios.interceptors.request.use(requestInterceptionFunction, (error) => {
        config.showLoading && closeLoading();
        Promise.reject(error);
    });
    axios.interceptors.response.use(
        (response: any) => responseInterceptionFunction(response, axios),
        (error) => {
            config && removePending(config);
            config.showLoading && closeLoading();
            httpErrorStatusHandle(error);
            return Promise.reject(error); // 错误继续返回给到具体页面
        }
    );
};

/**
 * Axios基础配置
 */
const axiosConfig = {
    baseURL: VITE_APP_BASE_API,
    // baseURL: import.meta.env.VITE_API_URL as string,
    timeout: 20 * 1000, // 请求超时时间20s
    withCredentials: true, // 是否需要跨域携带cookie
};

/**
 * 请求方法
 * @param reqOptions
 */
const REQ = function <T = unknown>(
    reqOptions: AxiosRequestConfig & ReqOptions
): Promise<T> {
    // eslint-disable-next-line
    let { method = "POST", url, data = {}, ...options } = reqOptions;
    options = { ...defaultOptions, ...options };
    options.removeEmptyStringParameters &&
        (data = handleRemoveEmptyParameter(data));
    options.showLoading && openLoading();
    // 创建实例
    const axios = Axios.create(axiosConfig);

    // 应用拦截
    useAxiosInterceptors(axios, reqOptions);
    return axios({
        method,
        url,
        [method.toLowerCase() === "get" ? "params" : "data"]: data,
        ...options, // 将配置参数传递，供拦截函数使用
    }) as Promise<T>;
};

export default REQ;
